/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppOptimizationLevelFlag

CppOptimizationLevelFlag::CppOptimizationLevelFlag(CppOptimizationLevelFlag::CppOptimizationLevel level)
	: m_level(level)
{
}

CppOptimizationLevelFlag::~CppOptimizationLevelFlag()
{
}

CppCompileFlag* CppOptimizationLevelFlag::Clone() const
{
	return new CppOptimizationLevelFlag(m_level);
}

string CppOptimizationLevelFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
	{
		switch(m_level)
		{
		case OPT_LEVEL_NONE:
			return "-O0";
			
		case OPT_LEVEL_LOW:
			return "-O1";
			
		case OPT_LEVEL_HIGH:
			return "-O2";
			
		case OPT_LEVEL_MAX:
			return "-O3";
			
		case OPT_LEVEL_SIZE:
			return "-Os";
		
		default:
			return "";
		}
	}
	else
		FatalError("CppOptimizationLevelFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppDebugInfoFlag

CppDebugInfoFlag::CppDebugInfoFlag()	
{
}

CppDebugInfoFlag::~CppDebugInfoFlag()
{
}

CppCompileFlag* CppDebugInfoFlag::Clone() const
{
	return new CppDebugInfoFlag();
}

string CppDebugInfoFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-g";
	else
		FatalError("CppDebugInfoFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppFunctionSectionFlag

CppFunctionSectionFlag::CppFunctionSectionFlag()	
{
}

CppFunctionSectionFlag::~CppFunctionSectionFlag()
{
}

CppCompileFlag* CppFunctionSectionFlag::Clone() const
{
	return new CppFunctionSectionFlag();
}

string CppFunctionSectionFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-ffunction-sections";
	else
		FatalError("CppFunctionSectionFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppMachineFlag

CppMachineFlag::CppMachineFlag(string machine)
	: m_machine(machine)	
{
}

CppMachineFlag::~CppMachineFlag()
{
}

CppCompileFlag* CppMachineFlag::Clone() const
{
	return new CppMachineFlag(m_machine);
}

string CppMachineFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return string("-") + m_machine;
	else
		FatalError("CppMachineFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppPositionIndependentCodeFlag

CppPositionIndependentCodeFlag::~CppPositionIndependentCodeFlag()
{

}

CppCompileFlag* CppPositionIndependentCodeFlag::Clone() const
{
	return new CppPositionIndependentCodeFlag;
}

string CppPositionIndependentCodeFlag::toString(const CppToolchain* toolchain)
{
	const GnuCppToolchain* gcc = dynamic_cast<const GnuCppToolchain*>(toolchain);
	if(gcc != NULL)
	{
		//mingw is always position independent
		if(gcc->IsWindowsToolchain())
			return "";
		else
			return "-fPIC";
	}
	else
		FatalError("CppPositionIndependentCodeFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppOpenMPFlag

CppOpenMPFlag::~CppOpenMPFlag()
{

}

CppCompileFlag* CppOpenMPFlag::Clone() const
{
	return new CppOpenMPFlag;
}

string CppOpenMPFlag::toString(const CppToolchain* toolchain)
{
	const GnuCppToolchain* gcc = dynamic_cast<const GnuCppToolchain*>(toolchain);
	if(gcc != NULL)
	{
		//mingw is always position independent
		if(gcc->IsWindowsToolchain())
			return "";
		else
			return "-fopenmp";
	}
	else
		FatalError("CppOpenMPFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppVisibilityCodeFlag

CppVisibilityCodeFlag::CppVisibilityCodeFlag(bool exportAllSymbols)
	: m_exportAllSymbols(exportAllSymbols)
{
}

CppVisibilityCodeFlag::~CppVisibilityCodeFlag()
{
}

CppCompileFlag* CppVisibilityCodeFlag::Clone() const
{
	return new CppVisibilityCodeFlag(m_exportAllSymbols);
}

string CppVisibilityCodeFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return m_exportAllSymbols ? "-fvisibility=default" : "-fvisibility=hidden";
	else
		FatalError("CppVisibilityCodeFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppIncludeDirectoryFlag

CppIncludeDirectoryFlag::CppIncludeDirectoryFlag(string path)
	: m_path(path)	
{
}

CppIncludeDirectoryFlag::~CppIncludeDirectoryFlag()
{
}

CppCompileFlag* CppIncludeDirectoryFlag::Clone() const
{
	return new CppIncludeDirectoryFlag(m_path);
}

string CppIncludeDirectoryFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return string("-I \"") + m_path + "\"";
	else
		FatalError("CppIncludeDirectoryFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppStandardFlag

CppStandardFlag::CppStandardFlag(CppStandardFlag::CppStandard std)
	: m_standard(std)
{
}

CppStandardFlag::~CppStandardFlag()
{
}

CppCompileFlag* CppStandardFlag::Clone() const
{
	return new CppStandardFlag(m_standard);
}

string CppStandardFlag::toString(const CppToolchain* toolchain)
{
	const GnuCppToolchain* gcc = dynamic_cast<const GnuCppToolchain*>(toolchain);
	if(gcc != NULL)
	{
		switch(m_standard)
		{
		case CPP_STANDARD_0X:
			return "-std=c++0x";
			break;
		case CPP_STANDARD_11:
			if(gcc->IsWindowsToolchain())	//workaround for debian mingw not supporting c++11 flag name yet
				return "-std=c++0x";
			else
				return "-std=c++11";
			break;
		case CPP_STANDARD_C99:
			return "-std=c99";
			break;
		case CPP_STANDARD_DEFAULT:
		default:
			return "";
		}
	}
	else
		FatalError("CppStandardFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppNoDelaySlotFlag

CppNoDelaySlotFlag::CppNoDelaySlotFlag()
{
}

CppNoDelaySlotFlag::~CppNoDelaySlotFlag()
{
}

CppCompileFlag* CppNoDelaySlotFlag::Clone() const
{
	return new CppNoDelaySlotFlag;
}

string CppNoDelaySlotFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-fno-delayed-branch";
	else
		FatalError("CppNoDelaySlotFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppNoBuiltinFlag

CppNoBuiltinFlag::CppNoBuiltinFlag()
{
}

CppNoBuiltinFlag::~CppNoBuiltinFlag()
{
}

CppCompileFlag* CppNoBuiltinFlag::Clone() const
{
	return new CppNoBuiltinFlag;
}

string CppNoBuiltinFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-fno-builtin";
	else
		FatalError("CppNoBuiltinFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppNoZeroCheckFlag

CppNoZeroCheckFlag::CppNoZeroCheckFlag()
{
}

CppNoZeroCheckFlag::~CppNoZeroCheckFlag()
{
}

CppCompileFlag* CppNoZeroCheckFlag::Clone() const
{
	return new CppNoZeroCheckFlag;
}

string CppNoZeroCheckFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-mno-check-zero-division";
	else
		FatalError("CppNoZeroCheckFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppProfilingFlag

CppProfilingFlag::CppProfilingFlag()
{
}

CppProfilingFlag::~CppProfilingFlag()
{
}

CppCompileFlag* CppProfilingFlag::Clone() const
{
	return new CppProfilingFlag;
}

string CppProfilingFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-fprofile-arcs -ftest-coverage";
	else
		FatalError("CppProfilingFlag is not supported on this compiler yet\n");
}
